import re
from SimpleParser import SimpleParser
from date_time import date_time_to_timestamp, now

class Country:
    def __init__(self, country_id="", abbr="", name=""):
        self.id = country_id
        self.abbr = abbr
        self.name = name
    def __str__(self):
        return '<country abbr="{0}" id="{1}" name="{2}" />'.format(self.abbr, self.id, self.name)
        
class City:
    def __init__(self, city_id="", country="", name=""):
        self.id = city_id
        self.country = country
        self.name = name
    def __str__(self):
        return '<city country="{0}" id="{1}" name="{2}" />'.format(self.country, self.id, self.name)

class Month:
    def __init__(self, month_id="", name=""):
        self.id = month_id
        self.name = name
    def __str__(self):
        return '<month id="{0}" name="{1}" />'.format(self.id, self.name)

class Rule:
    def __init__(self, rule_id="", name="", info=""):
        self.id = rule_id
        self.name = name
        self.info = info
    def __str__(self):
        return '<rule id="{0}" name="{1}" >\n<info>{2}</info>\n</rule>'.format(self.id, self.name, self.info)
    
class Opening:
    def __init__(self, opening_id="", name="", abbr=""):
        self.id = opening_id
        self.name = name
        self.abbr = abbr
    def __str__(self):
        return '<opening abbr="{0}" id="{1}" name="{2}" />'.format(self.abbr, self.id, self.name)

class Player:
    def __init__(self, player_id="", city="", country="", name="", surname=""):
        self.id = player_id
        self.city = city
        self.country = country
        self.name = name
        self.surname = surname
    def __str__(self):
        return '<player city="{0}" country="{1}" id="{2}" name="{3}" surname="{4}" />'.format(self.city, self.country, self.id, self.name, self.surname)
    def getLogStr(self):
        return "[{0}] {1} {2}".format(self.id, self.surname, self.name)

class Tournament:
    def __init__(self, tournament_id="", city="", country="", name="", start="", end="", rated="", rule="", month="", year=""):
        self.id = tournament_id
        self.city = city
        self.country = country
        self.name = name
        self.start = start
        self.end = end
        self.rated = rated
        self.rule = rule
        self.month = month
        self.year = year
    def __str__(self):
        return '<tournament city="{0}" country="{1}" end="{2}" id="{3}" month="{4}" name="{5}" rated="{6}" rule="{7}" start="{8}" year="{9}" />'.format(
                self.city, self.country, self.end, self.id, self.month, self.name, self.rated, self.rule, self.start, self.year)

class Game:
    def __init__(self, game_id="", alt="", black="", bresult="", btime="", opening="", publisher="", round="", rule="", swap="", tournament="", white="", wtime="", move="", info=""):
        self.id = game_id
        self.alt = alt
        self.black = black
        self.bresult = bresult
        self.btime = btime
        self.opening = opening
        self.publisher = publisher
        self.round = round
        self.rule = rule
        self.swap = swap
        self.tournament = tournament
        self.white = white
        self.wtime = wtime
        self.move = move
        self.info = info
    def __str__(self):
        res = '<game alt="{0}" black="{1}" bresult="{2}"'.format(self.alt, self.black, self.bresult)
        if self.btime != "":
            res += ' btime="{0}"'.format(self.btime)
        res += ' id="{0}" opening="{1}" publisher="{2}"'.format(self.id, self.opening, self.publisher)
        if self.round != "":
            res += ' round="{0}"'.format(self.round)
        res += ' rule="{0}" swap="{1}" tournament="{2}" white="{3}"'.format(self.rule, self.swap, self.tournament, self.white)
        if self.wtime != "":
            res += ' wtime="{0}"'.format(self.wtime)
        res += ' >\n<move>{0}</move>\n'.format(self.move)
        if self.info != "":
            res += "<info>{0}</info>\n".format(self.info)
        res += "</game>"
        return res

class Layout:
    def __init__(self, plural="", singular="", inst=None, missingBody=True):
        self.plural = plural
        self.singular = singular
        self.inst = inst
        self.missingBody = missingBody

class RIFXml:
    def __init__(self, fn):
        self.fn = fn
        self.country = dict()
        self.city = dict()
        self.month = dict()
        self.rule = dict()
        self.opening = dict()
        self.player = dict()
        self.tournament = dict()
        self.game = dict()
        self.maxId = dict()
        self.date = ""
        self.layout = [Layout("countries", "country", Country, True),
                       Layout("cities", "city", City, True),
                       Layout("months", "month", Month, True),
                       Layout("rules", "rule", Rule, False),
                       Layout("openings", "opening", Opening, True),
                       Layout("players", "player", Player, True),
                       Layout("tournaments", "tournament", Tournament, True),
                       Layout("games", "game", Game, False),
                       ]
        
    def collectGids(self, tid):
        gids = []
        if self.tournament.has_key(tid):
            for gid in self.game.keys():
                game = self.game[gid]
                if game.tournament == tid:
                    gids.append(gid)
        return gids

    def copyPlayer(self, pid, rhs):
        player = rhs.player[pid]
        self.player[pid] = rhs.player[pid]
        self.city[player.city] = rhs.city[player.city]
        self.country[player.country] = rhs.country[player.country]
    
    def copyGame(self, gid, rhs):
        game = rhs.game[gid]
        self.copyPlayer(game.black, rhs)                   
        self.copyPlayer(game.white, rhs)                   
        self.rule[game.rule] = rhs.rule[game.rule]
        self.game[gid] = rhs.game[gid]
        self.game[gid].move = ""

    def buildGamesDict(self, gids):
        res = dict()
        for gid in gids:
            game = self.game[gid]
            p1 = self.player[game.black]
            p2 = self.player[game.white]
            if (game.bresult == ""):
                s = "{0} {1} [{2}] : {3} {4} [{5}]".format(p1.surname, p1.name, "N/A", p2.surname, p2.name, "N/A")
            else:
                s = "{0} {1} [{2}] : {3} {4} [{5:g}]".format(p1.surname, p1.name, game.bresult, p2.surname, p2.name , 1-float(game.bresult))
            if not s in res:
                res[s] = 0
            res[s] += 1
        return res
    
    def getGamesDiff(self, rhs, tid):
        res = ""
        g1 = self.buildGamesDict(self.collectGids(tid))
        g2 = rhs.buildGamesDict(rhs.collectGids(tid))
        saw2 = set()
        for g in sorted(g1.keys()):
            saw2.add(g)
            if g in g2: 
                if g1[g] == g2[g]:
                    continue
                else:
                    res += "  game diff {0}: was {1}, now {2} game(s)\n".format(g, g1[g], g2[g])
            else:
                res += "  deleted {0}\n".format(g)
                
        for g in sorted(g2.keys()):
            if g in saw2:
                continue
            res += "  added {0}\n".format(g)
        return res
    
    def showDiff(self, days, rhs):
        days = int(days) * 3600 * 24
        saw = set()
        n1 = now()
        print "Difference {0} -> {1}".format(self.date, rhs.date)
        for tid in self.tournament.keys():
            saw.add(tid)
            trn = self.tournament[tid]
            if n1 - date_time_to_timestamp(trn.end) <= days:
                if tid in rhs.tournament:
                    res = self.getGamesDiff(rhs, tid)
                    if res != "":
                        print "{0} {1} games diff".format(trn.id, trn.name)
                        print res,
                    if trn.name == rhs.tournament[tid].name:
                        if trn.end == rhs.tournament[tid].end:
                            pass
                        else:
                            print "{0} {1} new end is {2}".format(trn.id, trn.name, rhs.tournament[tid].end)
                    else:
                        print "{0} {1} new name is {2}".format(trn.id, trn.name, rhs.tournament[tid].name)
                else:
                    print "{0} {1} deleted tournament".format(trn.id, trn.name)
        for tid in rhs.tournament.keys():
            if tid in saw:
                continue
            trn = rhs.tournament[tid]
            diff = n1 - date_time_to_timestamp(trn.end) 
            if diff <= days and diff > 0 and int(trn.rated) > 0:
                print "Added tournament {0} {1}".format(trn.id, trn.name)
    
    def copyTournament(self, tid, rhs):
        tid = str(tid)
        if not rhs.tournament.has_key(tid):
            raise Exception("{0} has no tournament {1}".format(rhs.fn, tid))
        self.deleteTournament(tid)
        gids = rhs.collectGids(tid)
        print "Added tournament {0} {1}".format(tid, rhs.tournament[tid].name)
        self.tournament[tid] = rhs.tournament[tid]
        for gid in gids:
#            print "Adding game {0} {1} : {2}".format(gid, rhs.player[game.black].surname, rhs.player[game.white].surname)
            self.copyGame(gid, rhs)
        self.date = rhs.date
    
    def deleteTournament(self, tid):
        tid = str(tid)
        if self.tournament.has_key(tid):
            gids = self.collectGids(tid)
            for gid in gids:
                self.deleteGame(gid)
            print "Deleted tournament {0}".format(tid)
            del self.tournament[tid]
    
    def deleteGame(self, gid):
        gid = str(gid)
        if self.game.has_key(gid):
            game = self.game[gid]
            print "Deleted game {0} {1} : {2}".format(gid, self.player[game.black].surname, self.player[game.white].surname)
            del self.game[gid]
        else:
            raise Exception("Failed to delete gid " + gid)
        
    def fixGamePlayer(self, gid, wrongId, rightId):
        gid = str(gid)
        wrongId = str(wrongId)
        rightId = str(rightId)
        if not self.game.has_key(gid):
            raise Exception("Game {0} not found".format(gid))
        game = self.game[gid]
        if game.black == wrongId:
            game.black = rightId
        elif game.white == wrongId:
            game.white = rightId
        else:
            raise Exception("Game {0} player with id {1} not found".format(gid, wrongId))
        print "Game {0} changed player {1} --> {2}".format(gid, self.player[wrongId].getLogStr(), self.player[rightId].getLogStr())
        
    def fixPlayer(self, wrongId, rightId):
        wrongId = str(wrongId)
        rightId = str(rightId)
        
        for gid in self.game.keys():
            fixed = False;
            game = self.game[gid]
            if game.black == wrongId:
                game.black = rightId
                fixed = True
            elif game.white == wrongId:
                game.white = rightId
                fixed = True
            if fixed:
                print "Game {0} changed player {1} --> {2}".format(gid, self.player[wrongId].getLogStr(), self.player[rightId].getLogStr())
        
    def addGame(self, tid, round, black, white, bresult):
        if tid == 'max':
            tid = self.maxId['tournament']
        tid = str(tid)
        round = str(round)
        black = str(black)
        white = str(white)
        bresult = str(bresult)
        if not self.tournament.has_key(tid):
            raise Exception("Unknown tid " + tid)
        if not self.player.has_key(black):
            raise Exception("Unknown black " + black)
        if not self.player.has_key(white):
            raise Exception("Unknown black " + white)
        gid = str(int(self.maxId["game"]) + 1)
        game = Game(game_id=gid, black=black, bresult=bresult, publisher="178", round=round, rule="1", tournament=tid, white=white)
        res = "0.5 : 0.5"
        if bresult == "1":
            res = "1:0"
        elif bresult == "0": 
            res = "0:1"
        print "Added game {0} {1} : {2} => {3}".format(gid, self.player[game.black].surname, self.player[game.white].surname, res)
        self.game[gid] = game
        self.maxId["game"] = gid

    def newTournament(self, cityId, dt, name):
        if not cityId in self.city:
            raise Exception("cityId={0} is not valid".format(cityId))
        tid = str(int(self.maxId['tournament']) + 1)
        self.maxId['tournament'] = tid
        city = self.city[cityId]
        m = re.match("(\d\d\d\d)-(\d\d)-(\d\d)", dt)
        if m == None:
            raise Exception("date={0} is not valid".format(dt))
        month = m.group(2)
        year = m.group(1)

        trn = Tournament(tournament_id=tid, city=city.id, country=city.country, name=name, start=dt, end=dt, 
                rated='1', rule='1', month=month, year=year)

        self.tournament[tid] = trn

        print "Added tournament {0} {1}".format(trn.id, trn.name)
    
    def loadSpecific(self, data, plural, singular, inst, missingBody):
        if not hasattr(self, singular):
            raise Exception("Unknown attr {0}".format(singular))
        objDict = getattr(self, singular)
        if not self.maxId.has_key(singular):
            self.maxId[singular] = "0"
        m = re.match('(?s).*?<{0}>(.*?)<\/{0}>'.format(plural), data)
        c = []
        if m:
            if missingBody:
                c = re.findall('(?s)<{0}.*?/>'.format(singular), m.group(1))
            else:
                c = re.findall('(?s)<{0}.*?<\/{0}>'.format(singular), m.group(1))
        for elem in c:
            obj = inst()
            sp = SimpleParser(elem)
            sp.toObj(obj)
            objDict[obj.id] = obj
            if int(obj.id) > int(self.maxId[singular]):
                self.maxId[singular] = obj.id 
            
    
    def load(self):
        # print "Loading", self.fn
        s = open(self.fn, "r").read()
        for l in self.layout:
            self.loadSpecific(s, l.plural, l.singular, l.inst, l.missingBody)
        m = re.match('(?s).*?<database.*?date=\"(.*?)\">', s)
        if m:
            self.date = m.group(1)
        else:
            raise Exception("no date")
    def save(self, fn):
        # print "Saving", fn
        fh = open(fn, "w")
        fh.write("""<?xml version="1.0"?>
<database version="1.0" date="{0}">

<!--#####################################################################################################################-->
<!-- This is an xml version of RenjuNet games database, containing following: -->
<!-- The list of countries -->
<!-- The list of cities -->
<!-- The list of months -->
<!-- The list of rules -->
<!-- The list of openings -->
<!-- The list of players -->
<!-- The list of tournaments -->
<!-- The list of games -->

<!-- The following escapes inside the texts have to be decoded into original symbols while loading/converting this file: -->
<!-- &amp; &lt; &gt; &apos; &quot; &copy; -->

<!-- COPYRIGHT INFORMATION -->
<!-- RenjuNet &copy; 2001 -->
<!-- Source: http://www.renju.net/downloads/games.php -->
<!-- E-mail: web@renju.net -->
<!-- It is allowed to use this database for non-commercial purposes in the forms of OFFLINE databases only. -->
<!-- It is forbidden to use any contents of this database or its modifications in any website or ONLINE system. -->
<!--#####################################################################################################################-->

""".format(self.date))
        for l in self.layout:
            objDict = getattr(self, l.singular)
            fh.write("<{0}>\n".format(l.plural))
            for c in sorted([int(x) for x in objDict.keys()]):
                fh.write(str(objDict[str(c)]) + "\n")
            fh.write("</{0}>\n\n".format(l.plural))
        
        fh.write("</database>")
        fh.close()
    
if __name__ == "__main__":
    left = RIFXml("../database.rif")
    left.load()
    print "Max tournament id={0}".format(left.maxId["tournament"])
    print "Max game id={0}".format(left.maxId["game"])
    print "Max player id={0}".format(left.maxId["player"])
    left.save("../test.rif")
    
